import tkinter as tk
from tkinter import messagebox
from tkinter import ttk
from tkinter import filedialog
import os
from core.constant import *
from core.object import CheckButtonContainer
from conf import conf


class AutoLayout:
    def __init__(self, parent, label_config, button_config):
        self.parent = parent

        self.label_config = label_config
        self.button_config = button_config

        self.labels = {}
        self.buttons = {}

        self.label_frame = None
        self.button_frame = None

        # # 清空parent界面的内容
        # self.clear_frame()

        self.create_label_layout()
        self.create_button_layout()

    def clear_frame(self):
        """ 清空指定frame中的所有组件 """
        for widget in self.parent.winfo_children():
            widget.destroy()

    def create_label_layout(self):
        # label容器创建或重建
        self.label_frame = tk.Frame(self.parent)
        self.label_frame.pack()

        for row, (label_member, widget_category, options, default_indices) in enumerate(self.label_config):
            label_member: LabelMember = label_member
            # 通过预先分配给组件的像素宽度，计算Label，Entry，Button的width
            label_leb_width = calculate_char_count(conf.View.label_width.value, conf.View.font.value[1])
            # 创建并放置标签
            label = tk.Label(self.label_frame, text=label_member.value, width=label_leb_width)
            label.grid(row=row, column=0, sticky="ew")
            # 创建var
            var = tk.StringVar()
            # 根据 widget_category 创建对应的小部件
            if widget_category == WidgetCategory.Label:
                widget_leb_width = calculate_char_count(conf.View.widget_width.value, conf.View.font.value[1])

                widget = tk.Label(self.label_frame, textvariable=var, width=widget_leb_width)
                widget.grid(row=row, column=1, sticky="ew")
                self.labels[label_member] = widget
                # 填写默认值
                if len(options) > 0 and len(default_indices) > 0:
                    var.set(options[default_indices[0]])

            elif widget_category == WidgetCategory.Entry:
                widget_leb_width = calculate_char_count(conf.View.widget_width.value, conf.View.font.value[1])

                widget = tk.Entry(self.label_frame, textvariable=var, width=widget_leb_width)
                widget.grid(row=row, column=1, sticky="ew")
                self.labels[label_member] = widget
                # 填写默认值
                if len(options) > 0 and len(default_indices) > 0:
                    var.set(options[default_indices[0]])

            elif widget_category == WidgetCategory.Combobox:

                widget_leb_width = calculate_char_count(conf.View.widget_width.value, conf.View.font.value[1])
                widget = ttk.Combobox(self.label_frame, values=options, width=widget_leb_width)
                widget.grid(row=row, column=1)
                self.labels[label_member] = widget
                # 填写默认值
                if len(options) > 0 and len(default_indices) > 0:
                    widget.set(options[default_indices[0]])

            elif widget_category == WidgetCategory.Filedialog:
                button_width = 10

                widget_entry_leb_width = calculate_char_count(conf.View.widget_width.value - button_width, conf.View.font.value[1])
                widget_button_leb_width = calculate_char_count(button_width, conf.View.font.value[1])

                frame = tk.Frame(self.label_frame)
                widget = tk.Entry(frame, textvariable=var, width=widget_entry_leb_width)
                widget.pack(side=tk.LEFT)
                button = tk.Button(frame, text="...", command=lambda e=widget: self.browse_file(e), width=widget_button_leb_width)
                button.pack(side=tk.LEFT)
                frame.grid(row=row, column=1, sticky="ew")

                self.labels[label_member] = widget
                # 填写默认值
                if len(options) > 0 and len(default_indices) > 0:
                    var.set(options[default_indices[0]])

            elif widget_category == WidgetCategory.CheckButton:
                # 创建选择按钮
                c_b_cont = CheckButtonContainer()
                c_b_cont.set_frame(self.label_frame)
                c_b_cont.set_cont(options, default_indices)
                c_b_cont.frame.grid(row=row, column=1, sticky="nsew")
                self.labels[label_member] = c_b_cont

            elif widget_category == WidgetCategory.ScrolledText:
                scrolled_text, text = create_scrolled_text(self.label_frame, width=50, height=2)
                text.insert("1.0", options[default_indices[0]])
                scrolled_text.grid(row=row, column=1)
                self.labels[label_member] = text

    def update_label_layout(self, update_label_config):
        for row, (label_member, widget_category, options, default_indices) in enumerate(update_label_config):
            for k, v in self.labels.items():
                if label_member == k:
                    self.set_value(label_member, options, default_indices)

    def create_button_layout(self):
        # button容器创建或重建
        self.button_frame = tk.Frame(self.parent)
        self.button_frame.pack()
        for row, (button_member, widget_category, options) in enumerate(self.button_config):
            if widget_category == WidgetCategory.Button:
                # 创建指令按钮
                button = tk.Button(self.button_frame, text=button_member.value)
                button.pack(side="left", padx=5)
                self.buttons[button_member] = button

    def update_button_layout(self):
        pass

    def label_command(self, bind_change_behavior):
        for label_member_o, callback_o in bind_change_behavior:
            # 查找label_config中相应列
            is_deal = False
            for label_config_o in self.label_config:
                if label_config_o[0] == label_member_o:
                    if label_config_o[1] == WidgetCategory.Entry:
                        # auto_layout.labels[label_member_o].bind(callback_o)
                        pass
                    elif label_config_o[1] == WidgetCategory.Combobox:
                        self.labels[label_member_o].bind("<<ComboboxSelected>>", lambda event, c=callback_o: c())
                    elif label_config_o[1] == WidgetCategory.CheckButton:
                        # auto_layout.labels[label_member_o].bind(callback_o)
                        pass
                    elif label_config_o[1] == WidgetCategory.Filedialog:
                        # auto_layout.labels[label_member_o].bind(callback_o)
                        pass
                    else:
                        pass
                    is_deal = True
                    break
            if not is_deal:
                raise ValueError("未找到相应组件并关联回调函数。")

    def button_command(self, parent_view):
        for button_member, button in self.buttons.items():
            if button_member == ButtonMember.Ok:
                button.bind("<Button-1>", lambda event: parent_view.on_ok())
            elif button_member == ButtonMember.Run:
                button.bind("<Button-1>", lambda event: parent_view.on_run())
            elif button_member == ButtonMember.OnlyData:
                button.bind("<Button-1>", lambda event: parent_view.on_only_data())
            elif button_member == ButtonMember.OnlyReport:
                button.bind("<Button-1>", lambda event: parent_view.on_only_report())
            elif button_member == ButtonMember.Stop:
                button.bind("<Button-1>", lambda event: parent_view.on_stop())
            elif button_member == ButtonMember.Draw:
                button.bind("<Button-1>", lambda event: parent_view.on_draw())

    def browse_file(self, entry_widget):
        start_dir = os.path.join(os.getcwd(), "model")
        filename = filedialog.askopenfilename(initialdir=start_dir)
        entry_widget.delete(0, tk.END)
        entry_widget.insert(0, filename)

    def get_value(self, label_member):
        widget = self.labels.get(label_member)
        if isinstance(widget, tk.Entry) and not isinstance(widget, ttk.Combobox):
            return [widget.get()], [0]
        elif isinstance(widget, ttk.Combobox):
            values = widget["values"]
            value = widget.get()
            index = None
            for i, value_o in enumerate(values):
                if value == value_o:
                    index = i
                    break
            if index is None:
                return [value], [0]
            return values, [index]
        elif isinstance(widget, tk.Text):
            return [widget.get("1.0", tk.END).strip()], [0]
        elif isinstance(widget, CheckButtonContainer):
            return widget.get_cont()
        return None

    def set_value(self, label_member, options, default_indices):
        widget = self.labels.get(label_member)
        if isinstance(widget, tk.Entry) and not isinstance(widget, ttk.Combobox):
            widget.delete(0, tk.END)
            if options and default_indices:
                widget.insert(0, options[default_indices[0]])
        elif isinstance(widget, ttk.Combobox):
            for i in range(len(widget["values"])):
                widget.delete(0)
            widget["values"] = options
            if options:
                if default_indices:
                    widget.set(options[default_indices[0]])
                else:
                    widget.set('')
                    pass
            else:
                widget.set('')
            pass
        elif isinstance(widget, tk.Text):
            widget.delete("1.0", tk.END)
            if default_indices:
                if options:
                    widget.insert("1.0", options[default_indices[0]])
        elif isinstance(widget, CheckButtonContainer):
            widget.set_cont(options, default_indices)
        else:
            pass


def val2px(px_width, px_height, x_val_min, x_val_max, y_val_min, y_val_max, x_val_ls, y_val_ls):
    """
    将二维数据转换为将其绘制在目标窗口所对应的像素值，数据x值递增对应其像素值的递增，但数据y值的递增对应像素值的递减。
    :param px_width: 目标窗口的宽度（像素值）
    :param px_height: 目标窗口的高度（像素值）
    :param x_val_min: 数据组x值的最小值
    :param x_val_max: 数据组x值的最大值
    :param y_val_min: 数据组y值的最小值
    :param y_val_max: 数据组y值的最大值
    :param x_val_ls: 数据组的x值列表
    :param y_val_ls: 数据组的y值列表
    :return: 数据组完整呈现在目标窗口对应的像素值
    """
    # 计算比例因子
    x_scale = px_width / (x_val_max - x_val_min)
    y_scale = px_height / (y_val_max - y_val_min)

    # 将数据点转换为像素坐标
    px_coords = []
    for x_val, y_val in zip(x_val_ls, y_val_ls):
        # 计算x坐标的像素值
        x_px = (x_val - x_val_min) * x_scale

        # 计算y坐标的像素值。注意：y轴的方向是向下的，所以需要取反
        y_px = px_height - (y_val - y_val_min) * y_scale

        px_coords.append((x_px, y_px))

    return px_coords

def info_messagebox(info):
    messagebox.showinfo("信息", info)


def warning_messagebox(info):
    messagebox.showwarning("警告", info)


def askyesno_messagebox(info):
    response = messagebox.askyesno("确认", info)
    return response


# 创建滑轨文本框
def create_scrolled_text(parent, width=None, height=None):
    # 滑轨宽度
    scrollbar_width = 15
    # 滑轨文本框父容器
    scrolled_text_frame = tk.Frame(parent)
    # 创建垂直滚动条
    scrollbar_y = tk.Scrollbar(scrolled_text_frame, orient='vertical', width=scrollbar_width)
    scrollbar_y.pack(side=tk.RIGHT, fill=tk.Y)
    # 创建水平滚动条
    scrollbar_x = tk.Scrollbar(scrolled_text_frame, orient='horizontal', width=scrollbar_width)
    scrollbar_x.pack(side=tk.BOTTOM, fill=tk.X)
    if width is not None and height is not None:
        # 创建文本框，禁用自动换行
        text = tk.Text(scrolled_text_frame, wrap=tk.NONE, width=width-scrollbar_width, height=height-scrollbar_width,
                       yscrollcommand=scrollbar_y.set,
                       xscrollcommand=scrollbar_x.set)
        text.tag_config("red_text", foreground="red")

    else:
        # 创建文本框，禁用自动换行
        text = tk.Text(scrolled_text_frame, wrap=tk.NONE,
                       yscrollcommand=scrollbar_y.set,
                       xscrollcommand=scrollbar_x.set)
    text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

    # 配置滚动条
    scrollbar_y.config(command=text.yview)
    scrollbar_x.config(command=text.xview)

    return scrolled_text_frame, text


def calculate_char_count(pixel_width, font_size):
    """
    根据给定的像素宽度和字体大小，计算所能容纳的英文字符数量。
    注意：这个函数提供的是一个大致估计，实际可容纳的字符数量可能会根据具体字体有所不同。

    :param pixel_width: 容器的宽度（像素）
    :param font_size: 字体大小（点）
    :return: 可容纳的英文字符数量
    """
    # 一般情况下，一个字符的宽度大约是其字体大小的一半。这是一个估计值。
    char_width = font_size * 0.9
    char_count = pixel_width / char_width
    return int(char_count)


def frame_canvas_del(frame: tk.Frame):
    for widget in frame.winfo_children():
        if isinstance(widget, tk.Canvas):
            widget.delete("all")


